home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #2 / Amiga Plus CD - 2004 - No. 02.iso / AmiSoft / Comm / tcp / JabberwockySRC.lha / Jabberwocky / src / roster.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-04-29  |  15.3 KB  |  633 lines

  1. /*  Copyright (C) 2002 Tom Parker (tom@carrott.org),
  2.                        Matthias Münch (matthias@amigaworld.de)
  3.  
  4.     This program is free software; you can redistribute it and/or modify
  5.     it under the terms of the GNU General Public License as published by
  6.     the Free Software Foundation; either version 2 of the License, or
  7.     (at your option) any later version.
  8.  
  9.     This program is distributed in the hope that it will be useful,
  10.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.     GNU General Public License for more details.
  13.  
  14.     You should have received a copy of the GNU General Public License
  15.     along with this program; if not, write to the Free Software
  16.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17.  
  18.     In addition, as a special exception, Tom Parker and Matthias Münch give
  19.     permission to link the code of this program with a TCP stack of your 
  20.     choice, any official MUI libraries or classes and any custom MUI classes
  21.     that should be necessary for the operation of this program.  This 
  22.     exception also gives you permission to distribute linked combinations 
  23.     including this software with any of the before-mentioned libraries and 
  24.     classes.  You must obey the GNU General Public License in all respects for
  25.     all of the code used other than that provided by the before-mentioned 
  26.     libraries and classes.  As part of this exception you are obliged to 
  27.     follow the license terms of the before-mentioned libraries, this license
  28.     does not compel you to follow those terms, but if you do not then you may
  29.     not link with those libraries.  If you modify this file, you may extend
  30.     this exception to your version of the file, but you are not obligated to
  31.     do so.  If you do not wish to do so, delete this exception statement from
  32.     your version.
  33. */
  34. /*
  35. ** roster
  36. */
  37.  
  38. #include "common.h"
  39.  
  40. #include <stdlib.h>
  41. #include <string.h>
  42.  
  43. #include <MUI/NListview_mcc.h>
  44.  
  45. #include "pix/unavail.h"
  46. #include "pix/avail.h"
  47. #include "pix/chat.h"
  48. #include "pix/away.h"
  49. #include "pix/xaway.h"
  50. #include "pix/dnd.h"
  51. #include "pix/msg.h"
  52. #include "pix/error.h"
  53.  
  54. static Object *r_list = NULL;
  55.  
  56. static ULONG roster_new(struct IClass *cl, Object *obj, struct opSet *msg);
  57. static ULONG roster_dispose(struct IClass *cl,Object *obj,Msg msg);
  58. static MUI_LIST_DISP_DECL(roster_disp, juser u);
  59. static MUI_LIST_DEST_DECL(roster_dest, juser u);
  60. static MUI_LIST_COMP_DECL(roster_comp, juser u1, juser u2);
  61. static void roster_menu(struct rosterdata *data, int menu);
  62. static void roster_update_item(iks *x);
  63. static void roster_close_all_windows();
  64. static void roster_close_windows(juser u);
  65. static void roster_remove_window(Object *win);
  66.  
  67. MUI_DISPATCH(roster_dispatch)
  68. {
  69.     switch(msg->MethodID)
  70.     {
  71.         case OM_NEW:
  72.             return roster_new(cl, obj, (APTR)msg);
  73.  
  74.         case OM_DISPOSE:
  75.             return roster_dispose(cl, obj, (APTR)msg);
  76.  
  77.         case ROSTER_MENU:
  78.             roster_menu(INST_DATA(cl,obj), (int)MARG1);
  79.             return 0;
  80.  
  81.         case ROSTER_DOUBLECLICK:
  82.         {
  83.             juser u;
  84.             u_long tmp;
  85.             struct rosterdata *data = INST_DATA(cl,obj);
  86.             GetAttr(MUIA_NList_DoubleClick, obj, &tmp);
  87.             if(tmp == -1 || tmp == -2) return(0);
  88.             DoMethod(obj, MUIM_NList_GetEntry, tmp, &u);
  89.             if(!u) {
  90.                 return(0);
  91.             }    
  92.             /* FIX ME: different actions for different events */
  93.             if(u->event) {
  94.                 read_from(u);
  95.             } else {
  96.                 write_to(iks_id_print(u->id), NULL, NULL);
  97.             }
  98.             return 0;
  99.         }
  100.  
  101.         case ROSTER_CLOSEME:
  102.         {
  103.             Object *win = (Object *)MARG1;
  104.             DoMethod(gui.app, OM_REMMEMBER, win);
  105.             MUI_DisposeObject(win);
  106.             return 0;
  107.         }
  108.  
  109.     }
  110.     return DoSuperMethodA(cl,obj,msg);
  111. }
  112.  
  113.  
  114. static ULONG roster_new(struct IClass *cl, Object *obj, struct opSet *msg)
  115. {
  116.     static struct Hook dispHook = { {0,0}, &roster_disp, NULL, NULL };
  117.     static struct Hook destHook = { {0,0}, &roster_dest, NULL, NULL };
  118.     static struct Hook compHook = { {0,0}, &roster_comp, NULL, NULL };
  119.     struct rosterdata *data;
  120.  
  121.     obj = (Object *)DoSuperNew(cl,obj,
  122.         MUIA_ContextMenu, NULL,
  123.         InputListFrame,
  124.         MUIA_NList_Format, ",",
  125.         MUIA_NList_ForcePen, MUIV_NList_ForcePen_On,
  126.         MUIA_NList_DisplayHook, &dispHook,
  127.         MUIA_NList_DestructHook, &destHook,
  128.         MUIA_NList_CompareHook, &compHook,
  129.         MUIA_CycleChain, 1,
  130.         TAG_MORE, msg->ops_AttrList);
  131.  
  132.     if(!obj) return(0);
  133.  
  134.     data = INST_DATA(cl,obj);
  135.     r_list = obj;
  136.     data->list = obj;
  137.     data->popupmenu = NULL;
  138.     
  139.     data->unavail_pix = mui_pix("PROGDIR:Images/unavail.iff", &unavail_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  140.     data->avail_pix = mui_pix("PROGDIR:Images/avail.iff", &avail_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  141.     data->chat_pix = mui_pix("PROGDIR:Images/chat.iff", &chat_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  142.     data->away_pix = mui_pix("PROGDIR:Images/away.iff", &away_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  143.     data->xaway_pix = mui_pix("PROGDIR:Images/xaway.iff", &xaway_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  144.     data->dnd_pix = mui_pix("PROGDIR:Images/dnd.iff", &dnd_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  145.     data->msg_pix = mui_pix("PROGDIR:Images/msg.iff", &msg_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  146.     data->error_pix = mui_pix("PROGDIR:Images/error.iff", &error_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
  147.     
  148.     DoMethod(obj, MUIM_NList_UseImage, data->unavail_pix, 0, 0L);
  149.     DoMethod(obj, MUIM_NList_UseImage, data->avail_pix, 1, 0L);
  150.     DoMethod(obj, MUIM_NList_UseImage, data->chat_pix, 2, 0L);
  151.     DoMethod(obj, MUIM_NList_UseImage, data->away_pix, 3, 0L);
  152.     DoMethod(obj, MUIM_NList_UseImage, data->xaway_pix, 4, 0L);
  153.     DoMethod(obj, MUIM_NList_UseImage, data->dnd_pix, 5, 0L);
  154.     DoMethod(obj, MUIM_NList_UseImage, data->msg_pix, 6, 0L);
  155.     DoMethod(obj, MUIM_NList_UseImage, data->error_pix, 7, 0L);
  156.  
  157.     DoMethod(obj, MUIM_Notify, MUIA_NList_DoubleClick, MUIV_EveryTime, obj, 1, ROSTER_DOUBLECLICK);
  158.  
  159.  
  160.     return (ULONG)obj;
  161. }
  162.  
  163.  
  164. MUI_LIST_DISP_STATIC(roster_disp, juser u)
  165. {
  166.     if(u->event)
  167.     {
  168.         *array++ = "\33o[6]";
  169.     } else if (u->error) {
  170.         *array++ = "\33o[7]";
  171.     } else if(u->subs != JUSER_NONE) {
  172.         switch(u->show)
  173.         {
  174.         case IKS_SHOW_UNAVAILABLE: *array++ = "\33o[0]"; break;
  175.         case IKS_SHOW_AVAILABLE: *array++ = "\33o[1]"; break;
  176.         case IKS_SHOW_CHAT: *array++ = "\33o[2]"; break;
  177.         case IKS_SHOW_AWAY: *array++ = "\33o[3]"; break;
  178.         case IKS_SHOW_XA: *array++ = "\33o[4]"; break;
  179.         case IKS_SHOW_DND: *array++ = "\33o[5]"; break;
  180.         case IKS_SHOW_ERROR: *array++ = "\33o[7]"; break;
  181.         default:
  182.             *array++ = "\33o[1]"; break;
  183.         }
  184.     }
  185.     else
  186.     {
  187.         *array++ = "";
  188.     }
  189.     
  190.     if(u->name) {
  191.         *array = u->name;
  192.     } else {
  193.         /** @todo use new pool functions and use iks_pool_free */
  194.         static char *s = NULL;
  195.         
  196.         if (s) {
  197.             free(s);
  198.         }
  199.         
  200.         *array = s = strdup(convert_locale(iks_id_print(u->id)));
  201.     }
  202.     
  203.     {
  204.     static char style[10];
  205.     char styleindex = 0;
  206.     
  207.     style[0] = 0;
  208.     if (u->error) {
  209.         strcpy(style, MUIX_PH);
  210.     }
  211.     if (u->subs != JUSER_BOTH) {
  212.         strcpy(style + strlen(style), MUIX_I);
  213.     }
  214.     if (u->ask) {
  215.         strcpy(style + strlen(style), MUIX_B);
  216.     }
  217.     array[DISPLAY_ARRAY_MAX] = style;
  218.     }
  219.     return 0;
  220. }
  221.  
  222.  
  223. MUI_LIST_DEST_STATIC(roster_dest, juser u)
  224. {
  225.     if(!u) return(0);
  226.     iks_pool_delete(u->p);
  227.     return(0);
  228. }
  229.  
  230.  
  231. MUI_LIST_COMP_STATIC(roster_comp, juser u1, juser u2)
  232. {
  233.     if(u1->event && !u2->event) return(-1);
  234.     if(!u1->event && u2->event) return(1);
  235.  
  236.     if(u1->subs == JUSER_NONE && u2->subs != JUSER_NONE) return(1);
  237.     if(u1->subs != JUSER_NONE && u2->subs == JUSER_NONE) return(-1);
  238.  
  239.     if(u1->show == IKS_SHOW_UNAVAILABLE && u2->show != IKS_SHOW_UNAVAILABLE) return(1);
  240.     if(u1->show != IKS_SHOW_UNAVAILABLE && u2->show == IKS_SHOW_UNAVAILABLE) return(-1);
  241.  
  242.     if(u1->show > u2->show) return(1);
  243.     if(u2->show > u1->show) return(-1);
  244.  
  245.     if(!u1->name && u2->name) return(1);
  246.     if(u1->name && !u2->name) return(-1);
  247.  
  248.     if(!u1->name && !u2->name)
  249.     {
  250.         int i = iks_strcmp(u1->id->server, u2->id->server);
  251.         if(i != 0) return(i);
  252.         iks_strcmp(u1->id->user, u2->id->user);
  253.     }
  254.  
  255.     return(iks_strcmp(u1->name, u2->name));
  256. }
  257.  
  258.  
  259. static ULONG roster_dispose(struct IClass *cl,Object *obj,Msg msg)
  260. {
  261.     struct rosterdata *data = INST_DATA(cl,obj);
  262.  
  263.     if(data->unavail_pix) MUI_DisposeObject(data->unavail_pix);
  264.     if(data->avail_pix) MUI_DisposeObject(data->avail_pix);
  265.     if(data->chat_pix) MUI_DisposeObject(data->chat_pix);
  266.     if(data->away_pix) MUI_DisposeObject(data->away_pix);
  267.     if(data->xaway_pix) MUI_DisposeObject(data->xaway_pix);
  268.     if(data->dnd_pix) MUI_DisposeObject(data->dnd_pix);
  269.     if(data->msg_pix) MUI_DisposeObject(data->msg_pix);
  270.     if(data->error_pix) MUI_DisposeObject(data->error_pix);
  271.  
  272.     return(DoSuperMethodA(cl, obj, msg));
  273. }
  274.  
  275.  
  276. static void roster_menu(struct rosterdata *data, int menu)
  277. {
  278.     juser u;
  279.     iks *x;
  280.  
  281.     DoMethod(r_list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &u);
  282.  
  283. if(!u && menu == ROSTER_SEND) xfer_test();
  284.  
  285.     if(!u) return;
  286.  
  287.     switch(menu)
  288.     {
  289.     case ROSTER_READ:
  290.         read_from(u);
  291.         break;
  292.  
  293.     case ROSTER_CHAT:
  294.         chat_with(u);
  295.         break;
  296.  
  297.     case ROSTER_INFO:
  298.         userinfo_of(u);
  299.         break;
  300.  
  301.     case ROSTER_SEND:
  302.         xfer_share(u);
  303.         break;
  304.  
  305.     case ROSTER_ACCEPT:
  306.         x = iks_make_pres(IKS_TYPE_SUBSCRIBED, 0, iks_id_printx(u->id, IKS_ID_USER | IKS_ID_SERVER), NULL);
  307.         iks_send(net.parser, x);
  308.         iks_delete(x);
  309.         break;
  310.  
  311.     case ROSTER_SUBS:
  312.         // if the contact is a transport don't subscribe
  313.         if (u->id->user == NULL) {
  314.             break;
  315.         }
  316.         x = iks_make_pres(IKS_TYPE_SUBSCRIBE, 0, iks_id_printx(u->id, IKS_ID_USER | IKS_ID_SERVER), NULL);
  317.         iks_send(net.parser, x);
  318.         iks_delete(x);
  319.         break;
  320.  
  321.     case ROSTER_REMOVE:
  322.         roster_remove(u);
  323.         break;
  324.  
  325.     }
  326. }
  327.  
  328.  
  329. void roster_fetch(void)
  330. {
  331.     iks *x;
  332.  
  333.     x = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
  334.     iks_send(net.parser, x);
  335.     iks_delete(x);
  336. }
  337.  
  338.  
  339. void roster_reset(void)
  340. {
  341.     roster_close_all_windows();
  342.     DoMethod(r_list, MUIM_NList_Clear);
  343. }
  344.  
  345.  
  346. void roster_update(ikspak *pak)
  347. {
  348.     iks *x;
  349.     
  350.     con_debug("processing roster update");
  351.  
  352.     switch(pak->subtype)
  353.     {
  354.     case IKS_TYPE_RESULT:  // I think this should only happen when we go online
  355.         net.state = NET_ON;
  356.         gui_state(MSG_PRES_ONLINE);
  357.         x = iks_make_pres(IKS_TYPE_AVAILABLE, IKS_SHOW_AVAILABLE, NULL, NULL);
  358.         iks_send(net.parser, x);
  359.         iks_delete(x);
  360.  
  361.     case IKS_TYPE_SET:
  362.         x = iks_child(pak->query);
  363.         while(x)
  364.         {
  365.             if(iks_strcmp(iks_name(x), "item")==0)
  366.                 roster_update_item(x);
  367.             x = iks_next(x);
  368.         }
  369.         iks_delete(pak->x);
  370.         break;
  371.  
  372.     default:
  373.         con_debug("parse roster failed\n");
  374.         iks_delete(pak->x);
  375.     }
  376. }
  377.  
  378.  
  379. static void roster_update_item(iks *x)
  380. {
  381.     juser u;
  382.     ikspool *p;
  383.     iksid *id;
  384.     char *tmp;
  385.  
  386.     tmp = iks_find_attrib(x, "jid");
  387.     if(!tmp) return;
  388.  
  389.     p = iks_pool_new(512);
  390.     if(!p) return;
  391.     id = iks_id_new(p, tmp);
  392.  
  393.     u = roster_find(id);
  394.     if(u)
  395.     {
  396.         iks_pool_delete(p);
  397.         p = u->p;
  398.     }
  399.     else
  400.     {
  401.         u = iks_pool_alloc(p, sizeof(juser_struct));
  402.         if(!u) return;
  403.         memset(u, 0, sizeof(juser_struct));
  404.         u->p = p;
  405.  
  406.         u->id = id;
  407.         DoMethod(r_list, MUIM_NList_InsertSingle, u, MUIV_NList_Insert_Sorted);
  408.     }
  409.     
  410.     tmp = iks_find_attrib(x, "name");
  411.     if(tmp) {
  412.         u->name = iks_pool_strdup(p, convert_locale(tmp));
  413.     }
  414.     
  415.     u->ask = iks_find_attrib(x, "ask");
  416.     
  417.     tmp = iks_find_attrib(x, "subscription");
  418.     if(tmp)
  419.     {
  420.         if(strcmp(tmp, "none")==0)
  421.             u->subs = JUSER_NONE;
  422.         else if(strcmp(tmp, "to")==0)
  423.             u->subs = JUSER_TO;
  424.         else if(strcmp(tmp, "from")==0)
  425.             u->subs = JUSER_FROM;
  426.         else if(strcmp(tmp, "both")==0)
  427.             u->subs = JUSER_BOTH;
  428.         else if(strcmp(tmp, "remove")==0)
  429.         {
  430.             long pos = MUIV_NList_GetPos_Start;
  431.             DoMethod(r_list, MUIM_NList_GetPos, u, &pos);
  432.             if(pos!=MUIV_NList_GetPos_End)
  433.                 DoMethod(r_list, MUIM_NList_Remove, pos);
  434.             return;
  435.         }
  436.     }
  437.     
  438.     u->group = iks_strdup(iks_find_cdata(x, "group"));
  439.     if (u->group == 0) {
  440.         u->group = "";
  441.     }
  442.     
  443.     roster_refresh(u);
  444. }
  445.  
  446.  
  447. void roster_update_presence(ikspak *pak)
  448. {
  449.     juser u;
  450.     char *tmp;
  451.     
  452.     u = roster_find(pak->from);
  453.     if (!u) {
  454.         char buff[100];
  455.         snprintf(buff, 99, "got presence packet for user not on roster, user is '%s'", 
  456.                 pak->from ? iks_id_print(pak->from) : "null");
  457.         con_debug(buff);
  458.         return;
  459.     }
  460.  
  461.     u->show = pak->show;
  462.     tmp = iks_find_cdata(pak->x, "status");
  463.     if(tmp) u->status = iks_pool_strdup(u->p, tmp);
  464.     if(pak->from->resource)
  465.     {
  466.         u->id->resource = iks_pool_strdup(u->p, pak->from->resource);
  467.         u->id->full = NULL;
  468.     }
  469.     
  470.     if (iks_strcmp(iks_find_attrib(pak->x, "type"), "error") == 0) {
  471.         u->error = "error";
  472.     } else {
  473.         u->error = NULL;
  474.     }
  475.     
  476.     roster_refresh(u);
  477.  
  478.     iks_delete(pak->x);
  479. }
  480.  
  481.  
  482. juser roster_find(iksid *id)
  483. {
  484.     juser u, u2 = NULL;
  485.     int i=0;
  486.  
  487.     while(1)
  488.     {
  489.         DoMethod(r_list, MUIM_NList_GetEntry, i, &u);
  490.         if(!u) break;
  491.         if(iks_id_cmp(id, u->id) == 0) return(u);
  492.         if(!u2 && iks_id_cmpx(id, u->id, IKS_ID_USER | IKS_ID_SERVER) == 0) u2 = u;
  493.         i++;
  494.     }
  495.     if(u2) return(u2);
  496.  
  497.     return NULL;
  498. }
  499.  
  500.  
  501. juser roster_next(int i)
  502. {
  503.     juser u;
  504.  
  505.     DoMethod(r_list, MUIM_NList_GetEntry, i, &u);
  506.     return u;
  507. }
  508.  
  509.  
  510. juser roster_locate(iksid *id)
  511. {
  512.     juser u;
  513.  
  514.     u = roster_find(id);
  515.     if(!u)
  516.     {
  517.         ikspool *p;
  518.         p = iks_pool_new(512);
  519.         if(!p) return(NULL);
  520.         u = iks_pool_alloc(p, sizeof(juser_struct));
  521.         if(!u) return(NULL);
  522.         memset(u, 0, sizeof(juser_struct));
  523.         u->p = p;
  524.         u->id = id;
  525.         DoMethod(r_list, MUIM_NList_InsertSingle, u, MUIV_NList_Insert_Sorted);
  526.     }
  527.  
  528.     return u;
  529. }
  530.  
  531.  
  532. void roster_remove(juser u)
  533. {
  534.     con_debug("doing remove");
  535.  
  536.     roster_close_windows(u);
  537.     {
  538.         int parts = 0;
  539.         iks *x, *y;
  540.         x = iks_make_iq(IKS_TYPE_SET, IKS_NS_ROSTER);
  541.         y = iks_find(x, "query");
  542.         y = iks_insert(y, "item");
  543.         
  544.         // transports should have a resource, users, not
  545.         if (u->id->user == NULL) { 
  546.             parts = IKS_ID_USER | IKS_ID_SERVER | IKS_ID_RESOURCE;
  547.         } else {
  548.             parts = IKS_ID_USER | IKS_ID_SERVER;
  549.         }
  550.         iks_insert_attrib(y, "jid", iks_id_printx(u->id, parts));
  551.         iks_insert_attrib(y, "subscription", "remove");
  552.         iks_send(net.parser, x);
  553.         iks_delete(x);
  554.         
  555.         /* since we are now adding contacts without a resource this section */
  556.         /* should no longer be needed and was causing unusual responses     */
  557.         /* from the server which resulted in enforcer hits from the iksemel */
  558.         /* library, it has now been removed                                 */
  559.         /* will remove altogether when I have looked into the iksemel hits  */
  560.         
  561.         /* we remove any accidentally added contacts with a resource, still unsure how this happens */
  562.         /* the correct solution here is if there are two entries in the roster with the same jid */
  563.         /* but different reasources, they should be treated seperately, and then remove only */
  564.         /* the specified contact */
  565. /*        x = iks_make_iq(IKS_TYPE_SET, IKS_NS_ROSTER);
  566.         y = iks_find(x, "query");
  567.         y = iks_insert(y, "item");
  568.         iks_insert_attrib(y, "jid", iks_id_printx(u->id, IKS_ID_USER | IKS_ID_SERVER | IKS_ID_RESOURCE));
  569.         iks_insert_attrib(y, "subscription", "remove");
  570.         iks_send(net.parser, x);
  571.         iks_delete(x);
  572. */    }
  573. }
  574.  
  575.  
  576. void roster_refresh(juser u)
  577. {
  578.     long pos = MUIV_NList_GetPos_Start;
  579.  
  580.     DoMethod(r_list, MUIM_NList_Sort);
  581.     if(u)
  582.     {
  583.         DoMethod(r_list, MUIM_NList_GetPos, u, &pos);
  584.         if(pos != MUIV_NList_GetPos_End)
  585.             DoMethod(r_list, MUIM_NList_Redraw, pos);
  586.     }
  587. }
  588.  
  589. /**
  590.  * closes all windows associated with the roster
  591.  */
  592. void roster_close_all_windows() {
  593.     int tmp = 0;
  594.     int entry;
  595.  
  596.     get(r_list, MUIA_NList_Entries, &tmp);
  597.     if (tmp > 0) {
  598.         for (entry = 0; entry < tmp; entry++) {
  599.             roster_close_windows(roster_next(entry));
  600.         }
  601.     }
  602. }
  603.  
  604. /**
  605.  * close and dispose of any windows associated with the passed user
  606.  */
  607. void roster_close_windows(juser u) {
  608.  
  609.     if (u->infowin) {
  610.         roster_remove_window(u->infowin);
  611.         u->infowin = NULL;
  612.     }
  613.     if (u->readwin) {
  614.         roster_remove_window(u->readwin);
  615.         u->readwin = NULL;
  616.     }
  617.  
  618.     if (u->chatwin) {
  619.         roster_remove_window(u->chatwin);
  620.         u->chatwin = NULL;
  621.     }
  622. }
  623.  
  624. /**
  625.  * dispose of the specified window
  626.  */
  627. void roster_remove_window(Object *win)
  628. {
  629.     DoMethod(gui.app, OM_REMMEMBER, win);
  630.     MUI_DisposeObject(win);
  631.     return;
  632. }
  633.